package com.augurit.smartform.display;

import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.CDATASection;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import com.augurit.common.syslog.tool.SysTraceLogWriter;
import com.augurit.common.util.lang.StringUtils;
import com.augurit.smartform.constant.FormConstant;
import com.augurit.smartform.constant.FreeMarkerConstant;
import com.augurit.smartform.constant.PageConstant;
import com.augurit.smartform.constant.TypeConstant;
import com.augurit.smartform.database.SourceData;
import com.augurit.smartform.entity.SmPage;
import com.augurit.smartform.exception.FormException;
import com.augurit.smartform.exception.FormNotUniqueException;
import com.augurit.smartform.exception.FormNullException;
import com.augurit.smartform.factory.FreeMarkerFactory;
import com.augurit.smartform.factory.TagParserFactory;
import com.augurit.smartform.factory.TemplateFactory;
import com.augurit.smartform.service.ISmartFormService;
import com.augurit.smartform.util.XMLHelper;
import com.augurit.smartform.web.parser.PageMgmtAction;
import com.augurit.wf.bi.service.IWfBusTemplateService;
import com.augurit.wf.bi.service.IWfPageElementService;
import com.augurit.wf.bi.service.IWfTemplateElementRefService;
import com.augurit.wf.bi.web.form.WfPageElementForm;
import com.augurit.wf.common.entity.WfTemplateElementRef;

import freemarker.ext.dom.NodeModel;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

@Component
@Transactional
public class PageExecutor {
	@Autowired
	private ISmartFormService smartFormService;
	@Autowired
	private PageResolve pageResolve;
	@Autowired
	private TemplateFactory templateFactory;
	@Autowired
	private TagParserFactory parserFactory;
	
	private static final Logger log = LoggerFactory.getLogger(PageExecutor.class);
	
//	public void execute(Map params){
//		String sourceXmlStr=(String)params.get("sourceXmlStr");
//		String webRealPath=(String)params.get("absolutePath");
//		Page page=pageParser.paser(sourceXmlStr);
//		this.injectParamsToPage(params, page);
//		this.setPageTop(page);
//		this.setCommonMeta(page);
//		this.setPageTitle(page);
//		this.wrapJavascriptTag(page);
//		
//		this.generateSourceFtlFile(page);
//		
//		Configuration cfg=FreeMarkerFactory.getConfiguration(webRealPath);
//		this.setTagString(cfg, page);
//		
//		Map<String,String> root=new HashMap<String,String>();
//		this.convertToTagMap(page, root);
//		
//		this.generateDestJspFile(cfg,page,root);
//		
//		smartFormService.savePage(page);
		
//		Template template=this.getTemplate(cfg, "test.ftl");
//		Map root=new HashMap();
//		Map map=new HashMap();
//		map.put("a", "abc");
//		map.put("x", "xyz");
//		root.put("field1", page);
//		root.put("field2", 15);
//		root.put("field10", new Date());
//		root.put("field3", "安徽");
//		root.put("field4", map);
//		Writer out=new OutputStreamWriter(System.out);
//		try {
//			template.process(root, out);
//			out.flush();
//		} catch (TemplateException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
	
	/**
	 * 生成页面JSP、保存数据库记录等功能
	 * @param source
	 * cuigeqiang
	 */
	public String execute(String loginUserName,SourceData source) throws FormException{
		PageElement page=pageResolve.paser(source.getXml());
		
		SmPage smPageForLog=smartFormService.getSmPageByPageCode(page.getPageCode());
		
		this.checkPageCodeCondition(page);
		
		this.injectParamsToPage(source, page);
		this.setPageTop(page);
		this.setCommonMeta(page);
		this.setPageTitle(page);
		this.wrapJavascriptTag(page);
		
		this.generateSourceFtlFile(page);
		
		Configuration cfg=FreeMarkerFactory.getConfiguration(source.getAbsolutePath());
		this.parseByFtl(cfg, page);
		this.parseByJava(page);
		
		this.appendPageTop(page);
		this.appendJavascript(page);
		
		Map<String,String> root=new HashMap<String,String>();
		this.convertToTagMap(page, root);
		
		this.generateDestJspFile(cfg,page,root);
		
		SmPage smPage=smartFormService.savePage(page);
		
		//新增或修改的日志
		String msg="";
		if(smPageForLog==null){
			msg="新增了表单，表单编码为“"+smPage.getPageCode()+"”";
		}else{
			msg="修改了表单，表单编码为“"+smPage.getPageCode()+"”";
		}
		log.info(loginUserName+SysTraceLogWriter.MESSAGE_SEPARATOR+msg);
		
		String returnPath=PageConstant.PREFIX_PAGE_PATH+smPage.getPageCode();
		return returnPath;
	}
	
	/**
	 * 用FTL方式进行进行组件的解析
	 * @param cfg
	 * @param page
	 * cuigeqiang
	 */
	private void parseByFtl(Configuration cfg,PageElement page){
		this.setSysTagStr4Ftl(cfg, page);
	}
	
	/**
	 * 用JAVA方式进行进行组件的解析
	 * @param page
	 * cuigeqiang
	 */
	private void parseByJava(PageElement page){
		this.setSysTagStr4Java(page);
	}
	
	/**
	 * 根据文件名得到FREEMARKER的模板
	 * @param cfg
	 * @param templateName
	 * @return
	 * cuigeqiang
	 */
	private Template getTemplate(Configuration cfg,String templateName){
		Template template=null;
		try {
			template = cfg.getTemplate(templateName);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return template;
	}
	
	/**
	 * 执行FREEMARKER模板
	 * @param template
	 * @param map
	 * @param writer
	 */
	private void executeTemplate(Template template,Map root,Writer writer){
		try {
			template.process(root, writer);
		} catch (TemplateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 添加参数到页面中
	 * @param map
	 * @param page
	 * cuigeqiang
	 */
	private void injectParamsToPage(SourceData source, PageElement page){
		page.setAbsolutePath(source.getAbsolutePath());
		page.setSourceXmlStr(source.getXml());
		page.setSourceHtmlStr(source.getHtml());
		page.setSourceFtlStr(source.getFtl());
		page.setClientJavascript(source.getClientJs());
		page.setServerJavascript(source.getServerJs());
		
		if(StringUtils.isNotBlank(source.getTemplateId())){
			page.setTemplateId(Long.parseLong(source.getTemplateId()));
		}
	}
	
	/**
	 * 产生源HTML的FTL模板
	 * @param page
	 * cuigeqiang
	 */
	private void generateSourceFtlFile(PageElement page){
		String fileFullPath=page.getAbsolutePath()+page.getSourcePath();
		BufferedWriter bwriter=null;
		try {
			bwriter=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileFullPath),"UTF-8"));
			bwriter.write(page.getSourceFtlStr());
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				bwriter.flush();
				bwriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 设置标签字符串
	 * @param cfg
	 * @param page
	 * cuigeqiang
	 */
	private void setSysTagStr4Ftl(Configuration cfg,PageElement page){
		Map<String,TagElement> elements=page.getElements();
		Iterator it=elements.entrySet().iterator();
		while(it.hasNext()){
			Map.Entry entry=(Map.Entry)it.next();
			TagElement tag=(TagElement)entry.getValue();
			this.setTagString4Ftl(cfg, tag);
		}
	}
	
	/**
	 * 通过JAVA解析器设置标签字符串
	 * @param page
	 * cuigeqiang
	 */
	private void setSysTagStr4Java(PageElement page){
		Map<String,TagElement> elements=page.getElements();
		Iterator it=elements.entrySet().iterator();
		while(it.hasNext()){
			Map.Entry entry=(Map.Entry)it.next();
			TagElement tag=(TagElement)entry.getValue();
			TagParser tagParser=parserFactory.getParserSelector().getTagParser(tag.getType());
			if(tagParser!=null){
				String[] strArr=tagParser.parser(tag);
				if(strArr!=null&&strArr.length>=3){
					String pageTop=strArr[0];
					String js=strArr[1];
					String tagHtmlStr=strArr[2];
//					this.appendPageTop(page, pageTop);
//					this.appendJavascript(page, js);
					if(StringUtils.isNotBlank(pageTop)){
						page.getTagPageTop().put(tag.getType(), pageTop);
					}
					if(StringUtils.isNotBlank(js)){
						page.getTagJavascript().put(tag.getType(), js);
					}
					this.replaceTagHtmlStr(tag, tagHtmlStr);
				}
			}
		}
	}
	
	/**
	 * 设置标签字符串
	 * @param cfg
	 * @param tag
	 * cuigeqiang
	 */
	private void setTagString4Ftl(Configuration cfg,TagElement tag){
		String tagString=this.getTagString4Ftl(cfg, tag);
		this.replaceTagHtmlStr(tag, tagString);
	}
	
	/**
	 * 获得标签字符串
	 * @param cfg
	 * @param tag
	 * cuigeqiang
	 */
	private String getTagString4Ftl(Configuration cfg,TagElement tag){
		String tagString="";
//		String templatName=templateFactory.getTemplateSelector().getTemplate(tag.getType());
		String templateName=tag.getType()+FreeMarkerConstant.FTL_SUFFIX;
		String tagsPath=Thread.currentThread().getContextClassLoader().getResource(FreeMarkerConstant.CURRENT_TAGS).getPath();						//标签模板放到CLASSES目录下面
		tagsPath = tagsPath.replaceAll("%20", " ");			//把“%20” 替换成空格
		String realTemplatePath=tagsPath+templateName;
		if(templateName!=null&&new File(realTemplatePath).exists()){
			Template template=this.getTemplate(cfg, templateName);
			Map root=new HashMap();
			root.put(PageConstant.KEY_CMPELEMENT, tag);
			try {
				root.put(PageConstant.KEY_CMPXML, this.getNodeModel(tag.getXmlStr()));
			} catch (ParserConfigurationException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (SAXException e) {
				e.printStackTrace();
			}
			StringWriter sw=new StringWriter();
			this.executeTemplate(template, root, sw);
			tagString=sw.getBuffer().toString();
			try {
				sw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return tagString;
	}
	
	/**
	 * 设置根MAP的KEY和VALUE
	 * @param page
	 * @param map
	 * cuigeqiang
	 */
	private void convertToTagMap(PageElement page,Map<String,String> map){
		map.put(PageConstant.KEY_PAGETOP, page.getPageTop());
		map.put(PageConstant.KEY_TITLE, page.getTitle());
		map.put(PageConstant.KEY_COMMONMETA, page.getCommonMeta());
		map.put(PageConstant.KEY_JAVASCRIPT, page.getJavascript());
		map.put(PageConstant.KEY_REPEAT_START, PageConstant.REPEAT_START);
		map.put(PageConstant.KEY_REPEAT_END, PageConstant.REPEAT_END);
		map.put(PageConstant.KEY_REPEAT_ID_PARAM, PageConstant.AND_SEPERATOR+PageConstant.REPEAT_ID_PARAM);
		map.put(PageConstant.KEY_PAGER, PageConstant.PAGER);
		map.put(PageConstant.KEY_PAGER_TOP, PageConstant.PAGER_TOP);
		
		this.injectTagElementsToTagMap(page, map);
	}
	
	/**
	 * 将PAGE的ELEMENTS提取出来并转成MAP<STRING,STRING>格式
	 * @param page
	 * @param map
	 * cuigeqiang
	 */
	private void injectTagElementsToTagMap(PageElement page,Map<String,String> map){
		Map<String,TagElement> elements=page.getElements();
		Iterator it=elements.entrySet().iterator();
		while(it.hasNext()){
			Map.Entry entry=(Map.Entry)it.next();
			TagElement tag=(TagElement)entry.getValue();
			String key=(String)entry.getKey();
			String value=tag.getHtmlStr();
			map.put(key, value);
		}
	}
	
	/**
	 * 产生目标JSP文件
	 * @param page
	 * @param root
	 * cuigeqiang
	 */
	private void generateDestJspFile(Configuration cfg,PageElement page,Map<String,String> root){
		String fileFullPath=page.getAbsolutePath()+page.getJspPath();
		BufferedWriter bwriter=null;
		StringWriter sw=new StringWriter();
		
		try {
			bwriter=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileFullPath),"UTF-8"));
			Template template=this.getTemplate(cfg, page.getSourceName());
			this.executeTemplate(template, root, bwriter);
			
			this.executeTemplate(template, root, sw);
			page.setJspContent(sw.getBuffer().toString());												//保存目标JSP的内容
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				bwriter.flush();
				bwriter.close();
				sw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 设置页面的顶部
	 * @param page
	 * cuigeqiang
	 */
	private void setPageTop(PageElement page){
		StringBuffer pageTop=new StringBuffer();
		pageTop.append(PageConstant.PAGETOP);
		pageTop.append(System.getProperty("line.separator"));
		pageTop.append(PageConstant.TAGLIBS);
		pageTop.append(System.getProperty("line.separator"));
		page.setPageTop(pageTop.toString());
	}
	
	/**
	 * 设置页面的标题
	 * @param page
	 * cuigeqiang
	 */
	private void setPageTitle(PageElement page){
		page.setTitle(page.getCnName());
	}
	
	/**
	 * 引入基础的JS和CSS文件
	 * @param page
	 * cuigeqiang
	 */
	private void setCommonMeta(PageElement page){
		String commonMeta=PageConstant.COMMONMETA;
		if(FormConstant.WORKFLOW.equals(page.getFormType())){
			commonMeta+=System.getProperty("line.separator");
			commonMeta+=PageConstant.COMMONMETA_WF;
		}else if(FormConstant.OA.equals(page.getFormType())){
			commonMeta+=System.getProperty("line.separator");
			commonMeta+=PageConstant.COMMONMETA_EXT;
			commonMeta+=System.getProperty("line.separator");
			commonMeta+=PageConstant.COMMONMETA_WF;
			commonMeta+=System.getProperty("line.separator");
			commonMeta+=PageConstant.COMMONMETA_OA;
		}
		page.setCommonMeta(commonMeta);
	}
	
	/**
	 * 将JAVASCRIPT内容包装为JAVASCRIPT标签
	 * @param page
	 * cuigeqiang
	 */
//	private void wrapJavascriptTag(PageElement page){
//		Element element=XMLHelper.generateDom4jElement("script");
//		element.addAttribute("type", "text/javascript");
//		element.addText(System.getProperty("line.separator"));
//		element.addText(page.getClientJavascript());
//		element.addText(System.getProperty("line.separator"));
//		page.setJavascript(System.getProperty("line.separator")+element.toString()+System.getProperty("line.separator"));
//	}
	private void wrapJavascriptTag(PageElement page){
		String wrappedStr="<script type=\"text/javascript\">";
		wrappedStr+=System.getProperty("line.separator");
		wrappedStr+=page.getClientJavascript();
		wrappedStr+=System.getProperty("line.separator");
		wrappedStr+="</script>";
		page.setJavascript(System.getProperty("line.separator")+wrappedStr+System.getProperty("line.separator"));
	}
	
	/**
	 * 检查PAGECODE是否唯一性、合法性和不为空
	 * @param page
	 * @throws Exception
	 * cuigeqiang
	 */
	private void checkPageCodeCondition(PageElement page) throws FormException{
		if(StringUtils.isBlank(page.getPageCode())){
			throw new FormNullException(PageConstant.KEY_PAGE_CODE);
		}else{
			SmPage smPage=smartFormService.getSmPageByPageCode(page.getPageCode());
			if(smPage!=null&&!page.getPageId().equalsIgnoreCase(smPage.getPageId())){
				throw new FormNotUniqueException(PageConstant.KEY_PAGE_CODE);
			}
			SmPage oldPage=smartFormService.getSmPageByPageId(page.getPageId());
			if(oldPage!=null&&!page.getPageCode().equals(oldPage.getPageCode())){
				throw new FormException("不允许修改pageCode...");
			}
		}
	}
	
	/**
	 * 追加pageTop字符串
	 * @param page
	 * @param pageTop
	 * cuigeqiang
	 */
	private void appendPageTop(PageElement page){
		StringBuffer pageTopSb=new StringBuffer();
		Map<String,String> pageTopMap=page.getTagPageTop();
		Iterator<String> it=pageTopMap.values().iterator();
		while(it.hasNext()){
			String tagPageTop=it.next();
			pageTopSb.append(tagPageTop);
			if(it.hasNext()){
				pageTopSb.append(System.getProperty("line.separator"));
			}
		}
		String pageTop=pageTopSb.toString();
		
		if(StringUtils.isNotBlank(pageTop)){
			String oldPageTop=page.getPageTop();
			if(StringUtils.isNotBlank(oldPageTop)){
				oldPageTop+=System.getProperty("line.separator");
				oldPageTop+=pageTop;
			}else{
				oldPageTop=pageTop;
			}
			page.setPageTop(oldPageTop);
		}
	}
	
	/**
	 * 追加javascript字符串
	 * @param page
	 * @param pageTop
	 * cuigeqiang
	 */
	private void appendJavascript(PageElement page){
		StringBuffer jsSb=new StringBuffer();
		Map<String,String> jsMap=page.getTagJavascript();
		Iterator<String> it=jsMap.values().iterator();
		while(it.hasNext()){
			String tagJs=it.next();
			jsSb.append(tagJs);
			if(it.hasNext()){
				jsSb.append(System.getProperty("line.separator"));
			}
		}
		String js=jsSb.toString();
		
		if(StringUtils.isNotBlank(js)){
			String oldJs=page.getJavascript();
			if(StringUtils.isNotBlank(oldJs)){
				oldJs+=System.getProperty("line.separator");
				oldJs+=js;
			}else{
				oldJs=js;
			}
			page.setJavascript(oldJs);
		}
	}
	
	/**
	 * 追加标签html字符串
	 * @param page
	 * @param tag
	 * cuigeqiang
	 */
	private void appendTagHtmlStr(TagElement tag,String tagHtml){
		if(StringUtils.isNotBlank(tagHtml)){
			String oldTagHtml=tag.getHtmlStr();
			if(StringUtils.isNotBlank(oldTagHtml)){
				oldTagHtml+=System.getProperty("line.separator");
				oldTagHtml+=tagHtml;
			}else{
				oldTagHtml=tagHtml;
			}
			tag.setHtmlStr(oldTagHtml);
		}
	}
	
	/**
	 * 替换标签html字符串
	 * @param tag
	 * @param tagHtml
	 * cuigeqiang
	 */
	private void replaceTagHtmlStr(TagElement tag,String tagHtml){
		if(StringUtils.isNotBlank(tagHtml)){
			tag.setHtmlStr(tagHtml);
		}else{
			tag.setHtmlStr("");
		}
	}
	
	/**
	 * 将控件的XML字符串解析成NODEMODEL
	 * @param tagXml
	 * @return
	 * @throws ParserConfigurationException 
	 * @throws IOException 
	 * @throws SAXException 
	 */
	private NodeModel getNodeModel(String tagXml) throws ParserConfigurationException, IOException, SAXException{
		DocumentBuilderFactory docBuilderFactory = NodeModel.getDocumentBuilderFactory();
        DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(tagXml.getBytes("UTF-8")));
        NodeModel.removeComments(doc);
        NodeModel.removePIs(doc);
        NodeModel.mergeAdjacentText(doc);
        return NodeModel.wrap(doc);
	}
}
